package futbol.tacticas.concursantes.TacticaLDA2;

import futbol.tacticas.*;
import futbol.General;
import futbol.Partido;
import futbol.tacticas.concursantes.*;
import java.awt.Color;
import java.awt.Point;
import java.util.Random;
import java.util.Vector;

public class TacticaLDA2 implements Tactica{

    private int pIniciales[][]={
        {-152,0,6},
        {-100,-50,6},
        {-100,0,6},
        {-100,50,6},        
        {-60,-35,6},
        {-60,35,6},
        {20,55,6},
        {20,-55,6},
        {80,-55,7},
        {80,55,7},
        {90,0,7} 
    };
    
    private Color colorNumero;
    private Color colorCamisa;
    private Color colorPantalon;
    
    private Point bola;
    private Point bolaAnt;
    private Point contrarios[];
    private Point misJugadores[];
    private Point contrariosAnt[];
    private Point misJugadoresAnt[];
    
    private int turno;
    private int velJugadores[];
    private int velContrarios[];
    
    private double b;
    private double velBola;
    private double pendiente;
    
    private Vector<Comando> comandos;
   
    public TacticaLDA2()
    {
        colorCamisa = new Color((int)Math.round(Math.random() * 255), (int)Math.round(Math.random() * 255), (int)Math.round(Math.random() * 255));
        colorPantalon = new Color((int)Math.round(Math.random() * 255), (int)Math.round(Math.random() * 255), (int)Math.round(Math.random() * 255));
        colorNumero = new Color((int)Math.round(Math.random() * 255), (int)Math.round(Math.random() * 255), (int)Math.round(Math.random() * 255));
        
        velContrarios = new int[11];
        velJugadores = new int[11];
        turno = 0;
        
        comandos = new Vector();
    }
    
    public String getNombre(){
        return "LDA v2";
    }
    
    public Color getColor1(){
        return colorCamisa;
    }
    
    public Color getColor2(){
        return colorPantalon;
    }
    
    public Color getColorNumero(){
        return colorNumero;
    }
    
    public int getFuerza(int n){
        return pIniciales[n][2];
    }
    
    public int getXInicial(int n){
        return pIniciales[n][0];
    }
    
    public int getYInicial(int n){
        return pIniciales[n][1];
    }
    
    public Vector<Comando> getComandos(SituacionJugadores sj){
        comandos.clear();
        
        if (turno == 1)
        {
            getInfoJugadores(sj);
        }
        
        misJugadores = sj.getMisJugadores();
        contrarios = sj.getContrario();
        
        try
        {
            bolaAnt = bola;
            bola = sj.getBola();
            velBola = distancia(bola, bolaAnt);
            
            pendiente = ((double)(bola.getY() - bolaAnt.getY()))/((double)(bola.getX() - bolaAnt.getX()));
            b = (double)bola.getY() - (double)(pendiente * bola.getX());
        }
        catch(Exception ex)
        {
            pendiente = Double.MAX_VALUE;
        }
        moverse(sj);
        tirar(sj);
        
        bola = sj.getBola();
        turno++;
        return comandos;
    }

    private int distancia(Point p1, Point p2) {
        return (int)Math.sqrt(Math.pow(p1.x-p2.x,2)+Math.pow(p1.y-p2.y,2));
    }

    private void getInfoJugadores(SituacionJugadores sj) {
        misJugadoresAnt = misJugadores;
        contrariosAnt = contrarios;
        
        misJugadores = sj.getMisJugadores();
        contrarios = sj.getContrario();
        for (int i = 10; i >= 0; i--)
        {
            velContrarios[i] = distancia(contrarios[i], contrariosAnt[i]);
            velJugadores[i] = distancia(misJugadores[i], misJugadoresAnt[i]);
        }
    }

    private void moverse(SituacionJugadores sj) {
        if(bola.getY() < 75 && bola.getY() > -75 && bola.getX()< -90 && sj.getMiJugadorMasCerca(bola.getX(), bola.getY()) == 0 && sj.estoyMasCercaDeBola() && velBola < 10)
        {
            comandos.add(new ComandoIrA(0, bola.getX(), bola.getY()));
        }
        else if(bola.getX() < 0 && pendiente != Double.MAX_VALUE && bolaAnt.x > bola.x)
        {
            double y = pendiente * misJugadores[0].getX() + b;
            
            if(y > 25) y = 25;
            else if(y < -25) y = -25;
            
            comandos.add(new ComandoIrA(0, pIniciales[0][0], y));
        }
        else
        {
            comandos.add(new ComandoIrA(0, pIniciales[0][0], pIniciales[0][1]));
        }
        
        int nJug = getJugadorMasCercanoBola();
        Point destinoNJug = getDestino(misJugadores[nJug], 4);
        comandos.add(new ComandoIrA(nJug,destinoNJug.getX(), destinoNJug.getY()));

        for (int i = 10; i > 0; i--)
        {
            if (i != nJug)
            {
                double distancia = distanciaPR(misJugadores[i]);
                if (distancia < 30)
                {
                    Point destino = getDestino(misJugadores[i], 4);
                    comandos.add(new ComandoIrA(i, destino.getX(), destino.getY()));
                }
                else if (i <= 3)
                {
                    int nJC=sj.getContrarioMasCerca(pIniciales[i][0], pIniciales[i][1]);
                    comandos.add(new ComandoIrA(i, contrarios[nJC].getX()+3*direccionBolaX(i), contrarios[nJC].getY()+3*direccionBolaY(i)));
                }
                else
                {
                    comandos.add(new ComandoIrA(i,pIniciales[i][0],pIniciales[i][1]));
                }
            }
        }
    }

    private void tirar(SituacionJugadores sj) {
        Despejar(sj);
        
        for (int i = 10; i > 0; i--)
        {
            if(i > 7)
            {
                comandos.add(new ComandoTiroAPuerta(i, 0));
            }
            else
            {
                Point result = MejorPunto(i);
                comandos.add(new ComandoGolpearBola(i, result.getX(), result.getY()));
            }
        }
    }

    private Point MejorPunto(int i) {
        int mejorPunt = Integer.MIN_VALUE;
        int xMej = misJugadores[i + 1].x, yMej = misJugadores[i+1].y;
        int punt;

        for (int n = i + 1; n < 11; n++)
        {
            punt = heuristica(i, n);
            if (mejorPunt < punt){
                mejorPunt = punt;
                xMej=misJugadores[n].x;
                yMej=misJugadores[n].y;
            }
        }
        return new Point(xMej, yMej);
    }

    private int heuristica(int i, int n) {
        int fuerza = pIniciales[i][2];
        int vel = fuerza*3;
        int velfinal = calcularVelocidadApunto(misJugadores[i], misJugadores[n], vel);
        if (velfinal > 17 || velfinal < 3)
        {
            return Integer.MIN_VALUE;
        }
        
        Point p = misJugadores[n];
        int punt = 0;
        double x = p.x;
        double y = p.y;
        boolean intercepta = false;
        if(x <= misJugadores[i].x)
        {
            intercepta = true;
        }
        else
        {
            for (int j = 0; j < 11; j++)
            {
                if (distancia(misJugadores[i], misJugadores[n]) > distancia(misJugadores[i], contrarios[j])
                    && calcularVelocidadApunto(misJugadores[i], contrarios[j], vel) <= 18
                    && distanciaPR2(misJugadores[i], misJugadores[n], contrarios[j]) <= 10) {
                    intercepta = true;
                    break;
                }
            }
        }
        if (intercepta)
        {
            punt -= 500;
        }
        else
        {
            punt += 500;
        }
        Point contrarioCercano = getJugadorMasCercano(p, contrarios);
        if (distancia(p, contrarioCercano) > 10)
        {
            punt += 1000;
        }
        else
        {
            punt -= 1000;
        }
        if(distancia(p, misJugadores[i]) > 150)
        {
            punt -= 1000;
        }
        else
        {
            punt += 1000;
        }
        if(x > misJugadores[i].x)
        {
            punt += 1000;
        }
        else
        {
            punt -= 1000;
        }
        return punt + (new Random()).nextInt(100);
    }

    private Point getJugadorMasCercano(Point p, Point[] jugadores) {
        Point cercano = new Point(0, 0);
        int distancia = Integer.MAX_VALUE;
        for(int i = 0; i < 11; i++)
        {
            int temp = distancia(p, jugadores[i]);
            if(distancia > temp)
            {
                distancia = temp;
                cercano = jugadores[i];
            }
        }
        return cercano;
    }

    private int getJugadorMasCercanoBola() {
        int cercano = 0;
        int distancia = Integer.MAX_VALUE;
        for(int i = 1; i < 11; i++)
        {
            int temp = distancia(bola, misJugadores[i]);
            if(distancia > temp)
            {
                distancia = temp;
                cercano = i;
            }
        }
        return cercano;
    }

    private int direccionBolaX(int i) {
        if(misJugadores[i].x == bola.x)
            return 0;
        if(misJugadores[i].x < bola.x)
            return 1;
        else
            return -1;
    }
    
    private int direccionBolaY(int i) {
        if(misJugadores[i].y == bola.y)
            return 0;
        if(misJugadores[i].y < bola.y)
            return 1;
        else
            return -1;
    }
    
    private double distanciaPR(Point p) {
        if(distancia(p, bolaAnt) > distancia(p, bola))
        {
            return (double)Math.abs(pendiente * p.getX() - p.getY() + b)/(double)Math.sqrt(Math.pow(pendiente, 2) + 1);
        }
        else 
        {
            return Double.MAX_VALUE;
        }
    }
    
    private double distanciaPR2(Point p0, Point p1, Point p2) {
        double pendienteTemp = ((double)(p1.getY() - p0.getY()))/((double)(p1.getX() - p0.getX()));
        double bTemp = (double)p1.getY() - (double)(pendienteTemp * p1.getX());
        
        return (double)Math.abs(pendienteTemp * p2.getX() - p2.getY() + bTemp)/(double)Math.sqrt(Math.pow(pendienteTemp, 2) + 1);
    }

    private Point getDestino(Point p, int desplazamiento) {
        if (velBola < 3)
        {
            return bola;
        }
        
        double x1, y1;
        double pendiente2 = -1/pendiente;
        double b2 = p.getY() - pendiente2 * p.getX();
        
        x1 = (b2 - b)/(pendiente - pendiente2);
        y1 = pendiente * x1 + b;
        
        Point destino = new Point((int)x1, (int)y1);
        
        if (distanciaPR(destino) == Double.MAX_VALUE)
        {
            return bola;
        }
        
        int vel;
        
        do {
            vel = calcularVelocidadApunto(bola, destino, (int)velBola);
            
            double desplazamientoX = desplazamiento;
            double desplazamientoY = desplazamiento;
            //puedo patearla, debo acercarme
            if (vel < 15)
            {
                if(bola.x < destino.x)
                {
                    desplazamientoX = desplazamientoX * -1;
                }
                if(bola.y < destino.y)
                {
                    desplazamientoY = desplazamientoY * -1;
                }
            }
            //va muy duro, debo alejarme para alcanzarla
            else
            {
                if(bola.x > destino.x)
                {
                    desplazamientoX = desplazamientoX * -1;
                }
                if(bola.y > destino.y)
                {
                    desplazamientoY = desplazamientoY * -1;
                }
            }
            destino.setLocation(destino.getX() + desplazamientoX, destino.getY() + desplazamientoY);
        } while (vel > 14);
        return destino;
    }
    

    private void Despejar(SituacionJugadores sj) {
        Point p0 = misJugadores[0];
        
        for (int i = 1; i < 6; i++)
        {
            Point p1 = misJugadores[i];
            boolean bueno = true;
            
            Point p2 = getJugadorMasCercano(p1, contrarios);
            
            double distancia = distanciaPR2(p0, p1, p2);
            
            int vel = calcularVelocidadApunto(p0, p1, pIniciales[0][2]);
            
            if((distancia < 12 && p2.getX() < p1.getX()) || (vel > 15 || vel == 0))
            {
                bueno = false;
            }
            if(bueno)
            {
                comandos.add(new ComandoGolpearBola(0, p1.getX(), p1.getY()));
                return;
            }
        }
        if (misJugadores[0].getY() > 0)
        {
            comandos.add(new ComandoGolpearBola(0, misJugadores[5].getX(), misJugadores[5].getY()));
        }
        else
        {
            comandos.add(new ComandoGolpearBola(0, misJugadores[4].getX(), misJugadores[4].getY()));
        }
    }
    
    private Point mejorarPunto(int i, Point p) {
        double pendiente2 = -1/pendiente;
        double b2 = p.getY() - pendiente2 * p.getX();
        
        double A = Math.pow(pendiente2, 2) + 1;
        
        double B = 2 * pendiente2 * b2 - 2 * p.getX() - 2 * pendiente2 * p.getY();
        
        double C = Math.pow(b2, 2) + Math.pow(p.getX(), 2) + Math.pow(p.getY(), 2) - 2 * p.getY() * b2 - 100;
                
        double disc = Math.pow(B, 2) - 4 * A * C;
        
        double sol1X = (-1 * B + Math.sqrt(disc)) / (2 * A);
        
        double sol1Y = pendiente2 * sol1X + b2;
        
        double sol2X = (-1 * B - Math.sqrt(disc)) / (2 * A);
        
        double sol2Y = pendiente2 * sol2X + b2;
        //
        if (Math.abs(sol1X) > Math.abs(sol2X))
        {
            return new Point ((int)sol1X, (int)sol1Y);
        }
        else
        {
            return new Point ((int)sol2X, (int)sol2Y);
        }
    }
    
    private int calcularVelocidadApunto(Point inicio, Point destino, int velocidad) {
        if (velocidad < 3)
        {
            return 0;
        }
        int dist = distancia(inicio, destino);
        
        while (dist > 0)
        {
            dist -= velocidad;
            velocidad--;
        }
        return Math.max(velocidad, 0);
    }
}
